#!/usr/bin/env python


#Import Modoules
import os,sys,math,traceback
import pygame, pygame.font, pygame.image, pygame.mixer, pygame.transform,pygame.sprite,pygame.constants
from pygame.locals import *
from ini import ini_file
from common import *
#from menu import *
from gamemap import *
from gui import *
from maploader import *

PROFILE = 0
PSYCO = 0
#----------------------------------------------------------

class SettingsBuilder:
    ' create a settings dict from a inifile and command line args '
    def __init__(self,map_creator,log=sys.stderr,inifile = r"./ini/turb.ini"):
        self.log = log
        self.inifile = inifile
        self.map_creator = map_creator

    def getSettings(self):
        # Create the nested settings dict
        settings = {}
        settings['sound'] = {'on':1,'channels':8}
        settings['network']= {'net_mode':'no_net',
                              'server_addr':None,
                              'server_port':None,
                              'client_port':None}
        settings['graphics'] = {'res':(641,480),
                                'parallax_layers':0,
                                'max_fps': 100,
                                'flags':0,
                                'bpp':0,
                                'hardware':0,
                                'fullscreen':0,
                                'doublebuff':0}
        settings['game'] = { 'no_of_players':2, 'players':[] }

        # Setup settings with inifile values
        if self.inifile:
            settings = self.__initSettingsFromFile(self.inifile,settings)


        #-------------------------------------------------------
        # Parse Command line
        #-------------------------------------------------------
        (argdict,args) = parseArgs('odfhn:b:p:s:')
        if args:
            self.log.write("unknown args:%s\n"%args)

        if (argdict.has_key('n') ):
            netopt = argdict['n']
            if netopt[0] == ' ':netopt=netopt[1:]
            if netopt=='server' or netopt =='s':
                settings['network']['net_mode']= 'server'
            elif netopt=='client' or netopt=='c':
                settings['network']['net_mode']= 'client'
            else:
                self.log.write("wrong args!\n")
                sys.exit()
        else:
            settings['network']['net_mode'] = 'no_net'

        if (argdict.has_key('d') ):
            settings['graphics']['doublebuff'] = 1
        if (argdict.has_key('f') ):
            settings['graphics']['fullscreen'] = 1
        if (argdict.has_key('h') ):
            settings['graphics']['hardware'] = 1
        if (argdict.has_key('b') ):
            settings['graphics']['bpp'] = int(argdict['b'])            
        if (argdict.has_key('p') ):
            settings['game']['no_of_players'] = int(argdict['p'])            
        if (argdict.has_key('s') ):
            settings['sound']['on'] = int(argdict['s'])            
        if (argdict.has_key('o') ): import gopsyco

        settings['game']['maps'] = self.map_creator.getAllMapNames()

        return settings

    def __initSettingsFromFile(self,file,settings):
        ' Reads and setup the settings hash from ini file'

        f = ini_file(file)

        # Get the graphics settings
        settings['graphics']['parallax_layers'] = int(f["Graphics"]["ParallaxLayers"])
        SCREEN_W,SCREEN_H = string.split(f["Graphics"]["Reslution"],"x")
        settings['graphics']['res'] = (int(SCREEN_W),int(SCREEN_H))
        settings['graphics']['max_fps'] = int(f["Graphics"]["MaxFPS"])

        # Get the sound settings
        settings['sound']['channels'] = int(f["Sound"]["Channels"])
        settings['sound']['on'] = int(f["Sound"]["On"])
        settings['sound']['FxVol'] = float(f["Sound"]["FxVol"])

        # Get the Network settings
#        settings['network']['server_addr'] = f[f["Network"]["Server"]]["Address"]
#        settings['network']['server_port'] = int(f[f["Network"]["Server"]]["Port"])
#        settings['network']['client_port'] = int(f["Network"]["ClientPort"])

        # Get the Game settings
        settings['game']['map']= f["Game"]["Map"]
        settings['game']['no_of_players'] = int(f["Game"]["Players"])

        # Set up player controls and name
        p = f["Players"].keys()
        p.sort()
        for player in p:
            name = f[player]['Name']
            cont = f[f[player]['Controls']]
            # Convert string def of keys to number
            for key in cont.keys():
                cont[key]=eval(cont[key])

            settings['game']['players'].append({'name':name,'controls':cont})

        return settings

class MapEditor:
    class Error(Exception):
        pass
    # Pallet for map bmp's
    map_pal = ((155, 155, 155), (0, 0, 0), (255, 0, 0), (0, 255, 0), (255, 255, 0), (0, 0, 240), (255, 160, 0), (214, 24, 206), (111, 19, 153), (24, 117, 109), (160, 160, 0), (0, 0, 0), (4, 0, 0), (59, 0, 86), (60, 191, 247), (132, 0, 0), (0, 0, 0), (42, 0, 0), (143, 0, 85), (103, 132, 52), (24, 0, 0), (38, 0, 86), (1, 191, 247), (229, 0, 0), (236, 1, 2), (184, 191, 249), (84, 191, 247), (152, 0, 0), (109, 0, 86), (143, 191, 247), (0, 132, 52), (52, 0, 0), (0, 62, 143), (70, 0, 0), (146, 0, 0), (170, 0, 2), (175, 0, 0), (55, 0, 0), (0, 1, 79), (55, 62, 143), (152, 1, 79), (207, 61, 118), (0, 0, 4), (0, 0, 0), (2, 147, 103), (85, 0, 42), (0, 0, 0), (0, 0, 132), (16, 61, 118), (1, 0, 0), (132, 0, 0), (255, 122, 100), (152, 62, 143), (207, 61, 189), (143, 255, 255), (0, 0, 1), (0, 62, 143), (218, 157, 148), (3, 0, 0), (2, 147, 103), (167, 158, 157), (42, 0, 0), (0, 0, 85), (60, 0, 132), (0, 0, 0), (2, 157, 148), (249, 22, 175), (0, 23, 143), (85, 0, 42), (132, 0, 0), (2, 157, 148), (2, 157, 148), (0, 0, 0), (211, 132, 220), (189, 132, 250), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 157, 148), (2, 156, 4), (0, 0, 0), (167, 41, 228), (189, 133, 26), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 156, 4), (2, 155, 44), (0, 0, 0), (232, 23, 167), (189, 133, 58), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 155, 44), (2, 152, 192), (0, 0, 0), (0, 0, 0), (189, 133, 90), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 152, 192), (20, 0, 0), (82, 133, 228), (1, 23, 167), (246, 0, 0), (13, 0, 100), (0, 0, 22), (60, 2, 0), (143, 134, 40), (26, 32, 74), (0, 0, 2), (42, 2, 0), (0, 0, 85), (100, 88, 246), (85, 0, 42), (0, 0, 0), (0, 0, 0), (0, 0, 0), (3, 49, 159), (0, 0, 0), (0, 0, 0), (159, 49, 159), (1, 0, 0), (190, 22, 183), (0, 3, 116), (0, 0, 0), (3, 0, 0), (0, 1, 17), (167, 0, 0), (13, 16, 227), (42, 0, 22), (13, 0, 85), (0, 0, 22), (1, 2, 0), (0, 0, 0), (159, 0, 0), (2, 157, 148), (159, 0, 0), (8, 6, 60), (0, 11, 124), (0, 0, 2), (0, 0, 1), (0, 2, 0), (40, 0, 0), (159, 62, 143), (88, 0, 0), (0, 22, 175), (84, 49, 159), (135, 158, 126), (1, 0, 0), (0, 0, 2), (0, 2, 0), (0, 0, 0), (143, 134, 40), (136, 152, 192), (60, 0, 88), (0, 2, 0), (22, 0, 13), (100, 88, 246), (85, 0, 42), (140, 0, 0), (88, 0, 2), (78, 30, 136), (1, 169, 141), (136, 30, 88), (92, 134, 92), (0, 170, 60), (0, 152, 192), (74, 49, 159), (159, 73, 126), (18, 0, 1), (12, 0, 0), (136, 0, 88), (0, 0, 32), (88, 0, 12), (251, 0, 0), (0, 108, 55), (57, 134, 172), (2, 155, 44), (2, 152, 192), (240, 22, 175), (12, 0, 0), (186, 0, 88), (36, 108, 55), (32, 0, 0), (48, 0, 0), (1, 0, 0), (192, 93, 225), (194, 0, 2), (167, 3, 219), (0, 0, 0), (36, 2, 0), (192, 0, 32), (207, 0, 2), (191, 135, 220), (87, 5, 151), (207, 0, 0), (103, 252, 122), (12, 6, 12), (36, 0, 32), (0, 0, 0), (0, 2, 0), (2, 0, 0), (0, 194, 204), (140, 0, 6), (217, 22, 207), (95, 191, 247), (0, 0, 0), (103, 0, 86), (71, 0, 0), (240, 191, 247), (0, 49, 216), (0, 49, 200), (223, 135, 28), (226, 1, 63), (1, 49, 200), (94, 1, 87), (63, 74, 233), (87, 19, 100), (0, 49, 216), (0, 49, 200), (86, 135, 94), (86, 135, 70), (0, 0, 0), (186, 0, 1), (0, 0, 1), (0, 49, 200), (87, 3, 7), (0, 0, 215), (94, 126, 160), (0, 135, 92), (152, 0, 12), (208, 0, 86), (52, 0, 14), (0, 0, 0), (145, 0, 89), (0, 191, 247), (52, 0, 88), (90, 9, 170), (0, 151, 162), (146, 0, 0), (170, 100, 58), (38, 2, 38), (223, 79, 77), (106, 0, 1), (207, 0, 0), (72, 135, 168), (170, 4, 95), (134, 0, 0), (162, 139, 202), (162, 151, 162), (24, 112, 0), (255, 0, 0), (255, 255, 255))

    def __init__(self,map_creator,log=sys.stderr):
        self.settings = None
        self.delay = 5
        self.log = log
        self.map_creator = map_creator
        self.clock = pygame.time.Clock()
        self.running = 0
        
    def initDisplay(self,settings):
        #-------------------------------------
        # Setup display
        SCREEN_W,SCREEN_H = settings['graphics']['res']
        ' calculates the flags to give to pygame diaplay '
        if settings['graphics']['doublebuff']:
            settings['graphics']['flags'] |= DOUBLEBUF
        else:
            settings['graphics']['flags'] &= ~DOUBLEBUF
        if settings['graphics']['fullscreen']:
            settings['graphics']['flags'] |= FULLSCREEN
        else:
            settings['graphics']['flags'] &= ~FULLSCREEN
        if     settings['graphics']['hardware']:
            settings['graphics']['flags'] |= HWSURFACE
        else:
            settings['graphics']['flags'] &= ~HWSURFACE
                
        flags = settings['graphics']['flags']
        bpp = settings['graphics']['bpp']

        # Init PyGame Display
        if bpp:    screen = pygame.display.set_mode((SCREEN_W, SCREEN_H), flags,bpp)
        else:    screen = pygame.display.set_mode((SCREEN_W, SCREEN_H), flags)
        pygame.display.set_caption('Turb')
        pygame.mouse.set_visible(0)

        # Set pyUI screen to new screen
        try:
            r = pyui.desktop.getRenderer()
        except AttributeError:
            pass
        else:
            r.screen = screen

        # Show display info
        if screen.get_flags() & HWSURFACE == HWSURFACE:
            print 'Hardware Accelerated'
        if screen.get_flags() & DOUBLEBUF == DOUBLEBUF:
            print 'Double Buffered'

        return screen
    
    def initMap(self,map_name):
        ' creates the map object and returns it '
        self.log.write('loading Map \"%s\"\n'%map_name)
        game_map = self.map_creator.getGameMap(map_name)
        return game_map

    def start(self,settings):
        ' implement this in derived classes '
        self.settings = settings

        self.screen = self.initDisplay(settings)
        self.game_map = self.initMap(settings['game']['map'])
        self.tile_w,self.tile_h = self.game_map.tile_w,self.game_map.tile_h


        self._run()

    def _run(self):
        ''' Runs the gameengine when every thing is initialyzed'''
        settings = self.settings
        screen = self.screen

        tile_w,tile_h = self.tile_w,self.tile_h
        # setup Viewports
        h = screen.get_height()
        w = screen.get_width()
        map_viewport = ViewPort(0,0,w,h,screen_pos=(0,0),screen=screen)

        #-------------------------------------
        # Setup game graphics
        forground_layer = self.map_creator.getTiledLayer(settings['graphics']['parallax_layers'])

        max_fps = settings['graphics']['max_fps']
        font = pygame.font.Font(None, 18)

        black = (0,0,0)
        clock = self.clock
        clock.tick()

        delay = self.delay # time to sleep between every frame
        self.running = 1
        keystate = pygame.key.get_pressed()
        (mouseposxold,mouseposyold)=pygame.mouse.get_pos()
        vx,vy = 0,0
        #------------------- Main Game loop -----------------
        while (self.running):
            # calculate time since last update 
            dt = float(clock.tick(max_fps))/1000

                
            # let operating system breath 
            pygame.time.delay(delay)

            # Clear screen 
            screen.fill(black)

            # we use the mouse to choose the speed of scroll...
            # just move the mouse a few pixel in all directions to understand
            # how to see the whole field
            (mouseposxnew,mouseposynew)=pygame.mouse.get_pos()
            if mouseposxnew>mouseposxold:
                vx=vx+1
            elif mouseposxnew<mouseposxold:
                vx=vx-1
            if mouseposynew>mouseposyold:
                vy=vy+1
            elif mouseposynew<mouseposyold:
                vy=vy-1
            mouseposxold,mouseposyold=mouseposxnew,mouseposynew

            # ------------- Draw game on screen ------------------
            # Update viewport position 
            map_viewport.centerx += vx
            map_viewport.centery += vy

            forground_layer.render(map_viewport)

            l=pygame.event.get()
            if len(l) and l[0].type == KEYDOWN:
                self.quit()

            # make changes visible 
            pygame.display.flip()


    def quit(self,save=0):
        self.log.write("Shutting down GameEngine\n")
        if save: self.save_map()

        self.running = 0

def main():
    log = sys.stderr
    map_creator = TileMapLoader(r'./Maps')
#        map_creator = MapLoader(r'./Maps')

    log.write("Parsing Settings ini file and command line\n")
    settings = SettingsBuilder(map_creator).getSettings()
    log.write("Starting pyUI\n")
    global gui
    gui = GUI(settings)
    settings = gui.run(fullscreen = settings['graphics']['fullscreen'])
    #settings['game']['map'] = map_creator.getAllMapNames()[0]
    editor = MapEditor(map_creator)

    while 1:
        if not pygame.display.get_init(): break
        pygame.mouse.set_visible(0)

        log.write("Starting Game\n")
        if PROFILE:
            import profile
            global editor,settings
            profile.run('editor.start(settings)')
        else:
            editor.start(settings)
        keystate = pygame.key.get_pressed() 

##        try:
##            game.startGame(settings)
##        except Exception,errmsg:
##            self.log.write('%s\n'%errmsg)

        pygame.mouse.set_visible(1)
        gui.run()
    editor.quit()
#----------------------------------------------------------
#    EOF
#----------------------------------------------------------

#this is python code to kickstart the program if not imported
if __name__ == '__main__':
    if PSYCO:
        try:
            import psyco
        except ImportError:
            print "Sorry,Psyco optimizer not found"
        else:
            from common import RenderToViewPort
            from gamemap import TiledLayer
            print "Using Psyco optimizer"
            psyco.bind(RenderToViewPort.draw)
            psyco.bind(RenderToViewPort.update)
            psyco.bind(TiledLayer.drawTiles)

    main()
    #profile.run('main()')
